
/*****************************************************************************\
 *                Test for polygon/polygon intersection check
\*****************************************************************************/

/*! @file
 *
 *  @brief
 *    Test polygon/polygon intersection check
 *
 *  Type './intersect -h' for an explanation of the command line options.
 *
 *
 *  The different tests are:
 *    -# 2 triangles touching in one vertex
 *    -# 2 triangles touching edge on vertex
 *    -# 2 parallel triangles not quite touching
 *    -# 2 triangles touching plane on vertex
 *    -# 2 triangles touching edge on plane
 *    -# triangle and quadrangle touching edge on plane
 *    -# triangle and quadstrip touching edge on plane
 *    -# test of some small triangles which was problematic in a user-scenario 
 *    -# 2 out of 1000 random triangles (this one is repeated automatically)
 *
 *  @author Gabriel Zachmann (zach@tu-clausthal.de)
 *	- reimplemented by Weiyu Yi, wyi@tu-clausthal.de
 *
 *  @todo
 *  - Check intersection of polygons where one of them is being transformed.
 *  - Neue naming conventions einbauen! (m_)
 *
 **/



//---------------------------------------------------------------------------
//  Includes
//---------------------------------------------------------------------------
#include <Collision.h>
#include <ColBoxtree.h>
#include <ColDopTree.h>
#include <ColTopology.h>
#include <ColUtils.h>
#include "ColUtilsOpenGL.h"

using col::Point3;
using col::ColGeometry;
using col::Primitive;
using col::Cube;
using col::Icosahedron;

using col::Vector3;
using col::Vector4;
using col::Matrix4;
using col::Boxtree;
using col::DopTree;

namespace
{

static const unsigned int MaxNumCol = 10000;

typedef enum
{
	PERSPECTIVE,
	ORTHOGRAPHIC	
} ProjectionE;

// here declaration of the global variables.
ColGeometry *geo[2];
col::Boxtree *boxtree[2];
vector<const std::vector<col::Point3> *> points[2];
Matrix4 moving_matrix;

// render options
bool with_window = false;
ProjectionE proj = PERSPECTIVE;
GLenum fill_mode = GL_LINE;

GLdouble view_distance = 3.0;
col::Trackball tball;
Matrix4 viewing_matrix;
GLuint w, h;

GLdouble fixed_color[3] = { 1.0, 0.7, 0.7 };
GLdouble moving_color[3] = { 0.7, 0.7, 1.0 };

Primitive tri_prim;
Primitive quad_prim;

}

/***************************************************************************\
 *                        Test cases                                       *
\***************************************************************************/

/// phase *must* start from 0 and increase step-wise by 1!
void testcase( int *phase )
{
try {
	int	result, result_wanted=-1;

	printf("\nphase %d\n", *phase );
	switch ( *phase )
	{
		case 0:
		{
			if ( geo[0] != NULL ) { delete geo[0]; geo[0] = NULL; }
			geo[0] = new ColGeometry();
			geo[0]->editPointsPtr()->resize( 3 );
			geo[0]->editPrimitivesPtr()->push_back( Primitive( tri_prim.begin(), tri_prim.end() ) );
			if ( geo[1] != NULL ) { delete geo[1]; geo[1] = NULL; }
			geo[1] = new ColGeometry();
			geo[1]->editPointsPtr()->resize( 3 );
			geo[1]->editPrimitivesPtr()->push_back( Primitive( tri_prim.begin(), tri_prim.end() ) );

			(*geo[0]->editPointsPtr())[0][0] = 0;  (*geo[0]->editPointsPtr())[0][1] = 0;  (*geo[0]->editPointsPtr())[0][2] = 0;
			(*geo[0]->editPointsPtr())[1][0] = 1;  (*geo[0]->editPointsPtr())[1][1] = 0;  (*geo[0]->editPointsPtr())[1][2] = 0;
			(*geo[0]->editPointsPtr())[2][0] = 0;  (*geo[0]->editPointsPtr())[2][1] = 1;  (*geo[0]->editPointsPtr())[2][2] = 0;

			(*geo[1]->editPointsPtr())[0][0] =  0;  (*geo[1]->editPointsPtr())[0][1] =  0;  (*geo[1]->editPointsPtr())[0][2] = 0;
			(*geo[1]->editPointsPtr())[1][0] = -1;  (*geo[1]->editPointsPtr())[1][1] =  0;  (*geo[1]->editPointsPtr())[1][2] = 0;
			(*geo[1]->editPointsPtr())[2][0] =  0;  (*geo[1]->editPointsPtr())[2][1] = -1;  (*geo[1]->editPointsPtr())[2][2] = 0;

			result_wanted = 1;

			break;
		}
		case 1:
			// 2 triangles touching edge on vertex
			if ( geo[0] != NULL ) { delete geo[0]; geo[0] = NULL; }
			geo[0] = new ColGeometry();
			geo[0]->editPointsPtr()->resize( 3 );
			geo[0]->editPrimitivesPtr()->push_back( Primitive( tri_prim.begin(), tri_prim.end() ) );
			if ( geo[1] != NULL ) { delete geo[1]; geo[1] = NULL; }
			geo[1] = new ColGeometry();
			geo[1]->editPointsPtr()->resize( 3 );
			geo[1]->editPrimitivesPtr()->push_back( Primitive( tri_prim.begin(), tri_prim.end() ) );

			(*geo[0]->editPointsPtr())[0][0] = 0;  (*geo[0]->editPointsPtr())[0][1] = 0;  (*geo[0]->editPointsPtr())[0][2] = 0;
			(*geo[0]->editPointsPtr())[1][0] = 1;  (*geo[0]->editPointsPtr())[1][1] = 0;  (*geo[0]->editPointsPtr())[1][2] = 0;
			(*geo[0]->editPointsPtr())[2][0] = 0;  (*geo[0]->editPointsPtr())[2][1] = 1;  (*geo[0]->editPointsPtr())[2][2] = 0;

			(*geo[1]->editPointsPtr())[0][0] = -1.0;  (*geo[1]->editPointsPtr())[0][1] =  1.0;  (*geo[1]->editPointsPtr())[0][2] = 0;
			(*geo[1]->editPointsPtr())[1][0] = -1.0;  (*geo[1]->editPointsPtr())[1][1] = -1.0;  (*geo[1]->editPointsPtr())[1][2] = 0;
			(*geo[1]->editPointsPtr())[2][0] =  1.0;  (*geo[1]->editPointsPtr())[2][1] = -1.0;  (*geo[1]->editPointsPtr())[2][2] = 0;

			result_wanted = 1;
			break;

		case 2:
			// 2 parallel triangles not quite touching
			if ( geo[0] != NULL ) { delete geo[0]; geo[0] = NULL; }
			geo[0] = new ColGeometry();
			geo[0]->editPointsPtr()->resize( 3 );
			geo[0]->editPrimitivesPtr()->push_back( Primitive( tri_prim.begin(), tri_prim.end() ) );
			if ( geo[1] != NULL ) { delete geo[1]; geo[1] = NULL; }
			geo[1] = new ColGeometry();
			geo[1]->editPointsPtr()->resize( 3 );
			geo[1]->editPrimitivesPtr()->push_back( Primitive( tri_prim.begin(), tri_prim.end() ) );

			(*geo[0]->editPointsPtr())[0][0] = 0;  (*geo[0]->editPointsPtr())[0][1] = 0.0;  (*geo[0]->editPointsPtr())[0][2] = 0.0;
			(*geo[0]->editPointsPtr())[1][0] = 1;  (*geo[0]->editPointsPtr())[1][1] = 0.1;  (*geo[0]->editPointsPtr())[1][2] = 0.2;
			(*geo[0]->editPointsPtr())[2][0] = 0;  (*geo[0]->editPointsPtr())[2][1] = 1.0;  (*geo[0]->editPointsPtr())[2][2] = 0.3;

			for ( int i = 0; i < 3; i ++ )
			{
				(*geo[1]->editPointsPtr())[i][0] = (*geo[0]->editPointsPtr())[i][0];
				(*geo[1]->editPointsPtr())[i][1] = (*geo[0]->editPointsPtr())[i][1];
				(*geo[1]->editPointsPtr())[i][2] = (*geo[0]->editPointsPtr())[i][2] + 1E-4;
			}

			result_wanted = 0;
			break;

		case 3:
			// 2 triangles touching plane on vertex
			if ( geo[0] != NULL ) { delete geo[0]; geo[0] = NULL; }
			geo[0] = new ColGeometry();
			geo[0]->editPointsPtr()->resize( 3 );
			geo[0]->editPrimitivesPtr()->push_back( Primitive( tri_prim.begin(), tri_prim.end() ) );
			if ( geo[1] != NULL ) { delete geo[1]; geo[1] = NULL; }
			geo[1] = new ColGeometry();
			geo[1]->editPointsPtr()->resize( 3 );
			geo[1]->editPrimitivesPtr()->push_back( Primitive( tri_prim.begin(), tri_prim.end() ) );

			(*geo[0]->editPointsPtr())[0][0] = 0;  (*geo[0]->editPointsPtr())[0][1] = 0;  (*geo[0]->editPointsPtr())[0][2] = 0;
			(*geo[0]->editPointsPtr())[1][0] = 1;  (*geo[0]->editPointsPtr())[1][1] = 0;  (*geo[0]->editPointsPtr())[1][2] = 0;
			(*geo[0]->editPointsPtr())[2][0] = 0;  (*geo[0]->editPointsPtr())[2][1] = 1;  (*geo[0]->editPointsPtr())[2][2] = 0;

			(*geo[1]->editPointsPtr())[0][0] = -1;  (*geo[1]->editPointsPtr())[0][1] =  1;  (*geo[1]->editPointsPtr())[0][2] =  1;
			(*geo[1]->editPointsPtr())[1][0] =  0;  (*geo[1]->editPointsPtr())[1][1] =  0;  (*geo[1]->editPointsPtr())[1][2] = -1;
			(*geo[1]->editPointsPtr())[2][0] =  1;  (*geo[1]->editPointsPtr())[2][1] = -1;  (*geo[1]->editPointsPtr())[2][2] =  1;

			result_wanted = 1;
			break;

		case 4:
			// 2 triangles touching plane on edge
			if ( geo[0] != NULL ) { delete geo[0]; geo[0] = NULL; }
			geo[0] = new ColGeometry();
			geo[0]->editPointsPtr()->resize( 3 );
			geo[0]->editPrimitivesPtr()->push_back( Primitive( tri_prim.begin(), tri_prim.end() ) );
			if ( geo[1] != NULL ) { delete geo[1]; geo[1] = NULL; }
			geo[1] = new ColGeometry();
			geo[1]->editPointsPtr()->resize( 3 );
			geo[1]->editPrimitivesPtr()->push_back( Primitive( tri_prim.begin(), tri_prim.end() ) );

			(*geo[0]->editPointsPtr())[0][0] = 0;  (*geo[0]->editPointsPtr())[0][1] = 0;  (*geo[0]->editPointsPtr())[0][2] = 0;
			(*geo[0]->editPointsPtr())[1][0] = 1;  (*geo[0]->editPointsPtr())[1][1] = 0;  (*geo[0]->editPointsPtr())[1][2] = 0;
			(*geo[0]->editPointsPtr())[2][0] = 0;  (*geo[0]->editPointsPtr())[2][1] = 1;  (*geo[0]->editPointsPtr())[2][2] = 0;

			(*geo[1]->editPointsPtr())[0][0] =  0;  (*geo[1]->editPointsPtr())[0][1] =  0;  (*geo[1]->editPointsPtr())[0][2] =  2;
			(*geo[1]->editPointsPtr())[1][0] =  0;  (*geo[1]->editPointsPtr())[1][1] = -1;  (*geo[1]->editPointsPtr())[1][2] = -1;
			(*geo[1]->editPointsPtr())[2][0] =  0;  (*geo[1]->editPointsPtr())[2][1] =  2;  (*geo[1]->editPointsPtr())[2][2] = -1;

			result_wanted = 1;

			break;

		case 5:
			// triangle and quadrangle touching
			if ( geo[0] != NULL ) { delete geo[0]; geo[0] = NULL; }
			geo[0] = new ColGeometry();
			geo[0]->editPointsPtr()->resize( 3 );
			geo[0]->editPrimitivesPtr()->push_back( Primitive( tri_prim.begin(), tri_prim.end() ) );
			if ( geo[1] != NULL ) { delete geo[1]; geo[1] = NULL; }
			geo[1] = new ColGeometry();
			geo[1]->editPointsPtr()->resize( 4 );
			geo[1]->editPrimitivesPtr()->push_back( Primitive( quad_prim.begin(), quad_prim.end() ) );

			(*geo[0]->editPointsPtr())[0][0] = 0;  (*geo[0]->editPointsPtr())[0][1] = 0;  (*geo[0]->editPointsPtr())[0][2] = 0;
			(*geo[0]->editPointsPtr())[1][0] = 1;  (*geo[0]->editPointsPtr())[1][1] = 0;  (*geo[0]->editPointsPtr())[1][2] = 0;
			(*geo[0]->editPointsPtr())[2][0] = 0;  (*geo[0]->editPointsPtr())[2][1] = 1;  (*geo[0]->editPointsPtr())[2][2] = 0;
			
			(*geo[1]->editPointsPtr())[0][0] =  0;  (*geo[1]->editPointsPtr())[0][1] =  1;  (*geo[1]->editPointsPtr())[0][2] =  1;
			(*geo[1]->editPointsPtr())[1][0] =  0;  (*geo[1]->editPointsPtr())[1][1] =  1;  (*geo[1]->editPointsPtr())[1][2] = -1;
			(*geo[1]->editPointsPtr())[2][0] =  0;  (*geo[1]->editPointsPtr())[2][1] = -1;  (*geo[1]->editPointsPtr())[2][2] = -1;
			(*geo[1]->editPointsPtr())[3][0] =  0;  (*geo[1]->editPointsPtr())[3][1] = -1;  (*geo[1]->editPointsPtr())[3][2] =  1;

			result_wanted = 1;

			break;

		case 6:
			// triangle and quadstrip touching
			if ( geo[0] != NULL ) { delete geo[0]; geo[0] = NULL; }
			geo[0] = new ColGeometry();
			geo[0]->editPointsPtr()->resize( 3 );
			geo[0]->editPrimitivesPtr()->push_back( Primitive( tri_prim.begin(), tri_prim.end() ) );
			if ( geo[1] != NULL ) { delete geo[1]; geo[1] = NULL; }
			geo[1] = new ColGeometry();
			geo[1]->editPointsPtr()->resize( 4 );
			geo[1]->editPrimitivesPtr()->push_back( Primitive( quad_prim.begin(), quad_prim.end() ) );

			(*geo[0]->editPointsPtr())[0][0] = 0;  (*geo[0]->editPointsPtr())[0][1] = 0;  (*geo[0]->editPointsPtr())[0][2] = 0;
			(*geo[0]->editPointsPtr())[1][0] = 1;  (*geo[0]->editPointsPtr())[1][1] = 0;  (*geo[0]->editPointsPtr())[1][2] = 0;
			(*geo[0]->editPointsPtr())[2][0] = 0;  (*geo[0]->editPointsPtr())[2][1] = 1;  (*geo[0]->editPointsPtr())[2][2] = 0;

			(*geo[1]->editPointsPtr())[0][0] =  0;  (*geo[1]->editPointsPtr())[0][1] =  1;  (*geo[1]->editPointsPtr())[0][2] =  1;
			(*geo[1]->editPointsPtr())[1][0] =  0;  (*geo[1]->editPointsPtr())[1][1] =  1;  (*geo[1]->editPointsPtr())[1][2] = -1;
			(*geo[1]->editPointsPtr())[2][0] =  0;  (*geo[1]->editPointsPtr())[2][1] = -1;  (*geo[1]->editPointsPtr())[2][2] =  1;
			(*geo[1]->editPointsPtr())[3][0] =  0;  (*geo[1]->editPointsPtr())[3][1] = -1;  (*geo[1]->editPointsPtr())[3][2] = -1;

			result_wanted = 1;

			break;
                    
        case 7 :
            //poly3.setActive( light_node, false );//de-select poly3
			if ( geo[0] != NULL ) { delete geo[0]; geo[0] = NULL; }
			geo[0] = new ColGeometry();
			geo[0]->editPointsPtr()->resize( 3 );
			geo[0]->editPrimitivesPtr()->push_back( Primitive( tri_prim.begin(), tri_prim.end() ) );
			if ( geo[1] != NULL ) { delete geo[1]; geo[1] = NULL; }
			geo[1] = new ColGeometry();
			geo[1]->editPointsPtr()->resize( 4 );
			geo[1]->editPrimitivesPtr()->push_back( Primitive( quad_prim.begin(), quad_prim.end() ) );

            (*geo[0]->editPointsPtr())[0][0] = -0.176454; (*geo[0]->editPointsPtr())[0][1] = 0.071328; (*geo[0]->editPointsPtr())[0][2] = 0.213297;
            (*geo[0]->editPointsPtr())[1][0] = -0.163126; (*geo[0]->editPointsPtr())[2][1] = 0.060675; (*geo[0]->editPointsPtr())[1][2] = 0.197186;
            (*geo[0]->editPointsPtr())[2][0] = -0.201797; (*geo[0]->editPointsPtr())[1][1] = 0.071328; (*geo[0]->editPointsPtr())[2][2] = 0.189499;

            (*geo[1]->editPointsPtr())[0][0] = -0.202112; (*geo[1]->editPointsPtr())[0][1] = 0.080522; (*geo[1]->editPointsPtr())[0][2] = 0.183896;
            (*geo[1]->editPointsPtr())[1][0] = -0.178983; (*geo[1]->editPointsPtr())[1][1] = 0.083684; (*geo[1]->editPointsPtr())[1][2] = 0.212995;
            (*geo[1]->editPointsPtr())[2][0] = -0.165755; (*geo[1]->editPointsPtr())[2][1] = 0.072958; (*geo[1]->editPointsPtr())[2][2] = 0.191836;
            
	        result_wanted = 0;
	        break;

        case 8:
			if ( geo[0] != NULL ) { delete geo[0]; geo[0] = NULL; }
			geo[0] = new ColGeometry();
			geo[0]->editPointsPtr()->resize( 3 );
			geo[0]->editPrimitivesPtr()->push_back( Primitive( tri_prim.begin(), tri_prim.end() ) );
			if ( geo[1] != NULL ) { delete geo[1]; geo[1] = NULL; }
			geo[1] = new ColGeometry();
			geo[1]->editPointsPtr()->resize( 3 );
			geo[1]->editPrimitivesPtr()->push_back( Primitive( tri_prim.begin(), tri_prim.end() ) );

            (*geo[0]->editPointsPtr())[0][0] = -0.163126; (*geo[0]->editPointsPtr())[0][1] = 0.060675; (*geo[0]->editPointsPtr())[0][2] = 0.197186;
            (*geo[0]->editPointsPtr())[1][0] = -0.186555; (*geo[0]->editPointsPtr())[1][1] = 0.060675; (*geo[0]->editPointsPtr())[1][2] = 0.175186;
            (*geo[0]->editPointsPtr())[2][0] = -0.201797; (*geo[0]->editPointsPtr())[2][1] = 0.071328; (*geo[0]->editPointsPtr())[2][2] = 0.189499;

            (*geo[1]->editPointsPtr())[0][0] = -0.202112; (*geo[1]->editPointsPtr())[0][1] = 0.080522; (*geo[1]->editPointsPtr())[0][2] = 0.183896;
            (*geo[1]->editPointsPtr())[1][0] = -0.178983; (*geo[1]->editPointsPtr())[1][1] = 0.083684; (*geo[1]->editPointsPtr())[1][2] = 0.212995;
            (*geo[1]->editPointsPtr())[2][0] = -0.165755; (*geo[1]->editPointsPtr())[2][1] = 0.072958; (*geo[1]->editPointsPtr())[2][2] = 0.191836;

	        result_wanted = 0;
	        break;

        case 9:
			if ( geo[0] != NULL ) { delete geo[0]; geo[0] = NULL; }
			geo[0] = new ColGeometry();
			geo[0]->editPointsPtr()->resize( 3 );
			geo[0]->editPrimitivesPtr()->push_back( Primitive( tri_prim.begin(), tri_prim.end() ) );
			if ( geo[1] != NULL ) { delete geo[1]; geo[1] = NULL; }
			geo[1] = new ColGeometry();
			geo[1]->editPointsPtr()->resize( 3 );
			geo[1]->editPrimitivesPtr()->push_back( Primitive( tri_prim.begin(), tri_prim.end() ) );

            (*geo[0]->editPointsPtr())[0][0] = -0.176454; (*geo[0]->editPointsPtr())[0][1] = 0.071328; (*geo[0]->editPointsPtr())[0][2] = 0.213297;
            (*geo[0]->editPointsPtr())[1][0] = -0.201797; (*geo[0]->editPointsPtr())[1][1] = 0.071328; (*geo[0]->editPointsPtr())[1][2] = 0.189499;
            (*geo[0]->editPointsPtr())[2][0] = -0.218691; (*geo[0]->editPointsPtr())[2][1] = 0.075000; (*geo[0]->editPointsPtr())[2][2] = 0.205365;

            (*geo[1]->editPointsPtr())[0][0] = -0.202112; (*geo[1]->editPointsPtr())[0][1] = 0.080522; (*geo[1]->editPointsPtr())[0][2] = 0.183896;
            (*geo[1]->editPointsPtr())[1][0] = -0.178983; (*geo[1]->editPointsPtr())[1][1] = 0.083684; (*geo[1]->editPointsPtr())[1][2] = 0.212995;
            (*geo[1]->editPointsPtr())[2][0] = -0.165755; (*geo[1]->editPointsPtr())[2][1] = 0.072958; (*geo[1]->editPointsPtr())[2][2] = 0.191836;

	        result_wanted = 0;
	        break;

        case 10:
			if ( geo[0] != NULL ) { delete geo[0]; geo[0] = NULL; }
			geo[0] = new ColGeometry();
			geo[0]->editPointsPtr()->resize( 3 );
			geo[0]->editPrimitivesPtr()->push_back( Primitive( tri_prim.begin(), tri_prim.end() ) );
			if ( geo[1] != NULL ) { delete geo[1]; geo[1] = NULL; }
			geo[1] = new ColGeometry();
			geo[1]->editPointsPtr()->resize( 3 );
			geo[1]->editPrimitivesPtr()->push_back( Primitive( tri_prim.begin(), tri_prim.end() ) );

            (*geo[0]->editPointsPtr())[0][0] = -0.018837; (*geo[0]->editPointsPtr())[0][1] = 0.075000; (*geo[0]->editPointsPtr())[0][2] = 0.299408;
            (*geo[0]->editPointsPtr())[1][0] = -0.056214; (*geo[0]->editPointsPtr())[1][1] = 0.075000; (*geo[0]->editPointsPtr())[1][2] = 0.294687;
            (*geo[0]->editPointsPtr())[2][0] = -0.060557; (*geo[0]->editPointsPtr())[2][1] = 0.071328; (*geo[0]->editPointsPtr())[2][2] = 0.317451;

            (*geo[1]->editPointsPtr())[0][0] = -0.049907; (*geo[1]->editPointsPtr())[0][1] = 0.086203; (*geo[1]->editPointsPtr())[0][2] = 0.296841;
            (*geo[1]->editPointsPtr())[1][0] = -0.057335; (*geo[1]->editPointsPtr())[1][1] = 0.080902; (*geo[1]->editPointsPtr())[1][2] = 0.326649;
            (*geo[1]->editPointsPtr())[2][0] = -0.043106; (*geo[1]->editPointsPtr())[2][1] = 0.084220; (*geo[1]->editPointsPtr())[2][2] = 0.338304;

	        result_wanted = 0;
	        break;

        case 11:
			// 2 out of 1000 random triangles
			if ( geo[0] != NULL ) { delete geo[0]; geo[0] = NULL; }
			geo[0] = new ColGeometry();
			geo[0]->editPointsPtr()->resize( 3 );
			geo[0]->editPrimitivesPtr()->push_back( Primitive( tri_prim.begin(), tri_prim.end() ) );
			if ( geo[1] != NULL ) { delete geo[1]; geo[1] = NULL; }
			geo[1] = new ColGeometry();
			geo[1]->editPointsPtr()->resize( 3 );
			geo[1]->editPrimitivesPtr()->push_back( Primitive( tri_prim.begin(), tri_prim.end() ) );

			for ( unsigned int i = 0; i < 3; i ++ )
				for ( unsigned int j = 0; j < 3; j ++ )
					(*geo[0]->editPointsPtr())[i][j] = col::pseudo_randomf();
					// should produce the same sequence every time

			for ( unsigned int i = 0; i < 3; i ++ )
				for ( unsigned int j = 0; j < 3; j ++ )
					(*geo[1]->editPointsPtr())[i][j] = col::pseudo_randomf();

			result_wanted = -1;							// = don't know
			break;

		default:
			// last phase has been reached, signal to caller
			printf( "last phase has been reached; starting again.\n" );
			*phase = -1;
	}
	(*phase) ++ ;

	try
	{
		// check intersection of the 2 triangles
		result = col::intersectPolygons( &( (*geo[0]->editPointsPtr())[0] ), geo[0]->editPointsPtr()->size(), 
										 &( (*geo[1]->editPointsPtr())[0] ), geo[1]->editPointsPtr()->size() );
	}
	catch ( col::XCollision /*&x*/ )
	{
		fputs("intersect: exception XCollision!\n", stderr );
	}

	if ( *phase )
	{
		if ( result_wanted == -1 )
		{
			printf("Polygons %s\n", result ? "intersect" : "don't intersect" );
		}
		else
		{
			printf("Polygons %s %s\n",
					result ? "intersect" : "don't intersect",
					( result != result_wanted ) ? "ERROR!" : " -> OK" );
		}
	} // else: we just had a wrap-around, i.e., we didn't really do a test
}
catch ( col::XCollision &x )
{
	x.print(stderr);
	exit(-1);					// not sensible to continue testing
}
}

void init(void) 
{
	glClearColor(0.0, 0.0, 0.0, 0.0);
	glShadeModel(GL_FLAT);
	glEnable(GL_DEPTH_TEST);
}

void reshape (int wide, int height)
{
	w = wide;
	h = height;
	glViewport(0, 0, (GLsizei) w, (GLsizei) h);
	tball.setViewport( w, h );
}

void display(void)
{
	// all geometries are ready to render
	// begin display
	
	// trackball 
	if ( tball.getTrackMode() == col::TRACK_ROTATE )
	{
		REAL angle;
		REAL axis[3];
		tball.getRotation( axis, angle );
		Matrix4 m = Matrix4::rotation( angle, col::Vector3( axis[0], axis[1], axis[2] ) );
		viewing_matrix = m * viewing_matrix;
	}
	else
	if ( tball.getTrackMode() == col::TRACK_ZOOM )
	{
		REAL zoom;
		tball.getZoom( zoom );
		Matrix4 m = Matrix4::scale( col::Vector3( zoom+1 ) );
		viewing_matrix = m * viewing_matrix;
	}
	
	// 1) Prjection Trasformation
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity ();

	GLdouble range=2.0;
	switch( proj )
	{
		case ORTHOGRAPHIC:
			// orthographic projection
			if (w <= h)
				glOrtho(-1*range, range, -1*range*(GLdouble)h/(GLdouble)w,
						range*(GLdouble)h/(GLdouble)w, 1.0, 5.0);
			else
				glOrtho(-1*range*(GLdouble)w/(GLdouble)h,
						range*(GLdouble)w/(GLdouble)h, -1*range, range, 1.0, 5.0);
			break;
		case PERSPECTIVE:
			// perspective projection
			gluPerspective(60, (GLdouble)w/(GLdouble)h, 1.0, 5.0);
			break;
		default:
			fprintf( stderr, "No such projection mode!\n" );
			break;
	}

	// 2) View Transformation
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

	// parameter with "-" cause they are defined as eye->world, here is used as world-eye
	// care about the sequence of the commands
	
	// zoom out or zoom in
	glTranslated( 0.0, 0.0, -view_distance );

	GLdouble view_mat[16];
	for ( unsigned int i=0; i<4; i++ )
	for ( unsigned int j=0; j<4; j++ )
		view_mat[ i*4+j ] = viewing_matrix[i][j];
	glMultMatrixd( view_mat );


	// 3) Modelling Transformation
	// drawing
	if ( geo[0] != NULL )
	{
		glPushMatrix();
		col::drawGeometry( geo[0], fill_mode, fixed_color );
		glPopMatrix();
	}

	if ( geo[1] != NULL )
	{
		glPushMatrix();
		col::drawGeometry( geo[1], fill_mode, moving_color );
		glPopMatrix();
	}

	//
	glutSwapBuffers();
}

void mouseClick( int button, int state, int x, int y )
{
	tball.mouseClick( button, state, x, y );
}

void mouseMoving( int x, int y )
{
	tball.mouseMoving( x, y );
	glutPostRedisplay();
}

void keyboard(unsigned char key, int x, int y)
{
	static int phase = 0;

	switch (key) {
		case 27:
			exit(0);
			break;
		case ' ':
			testcase( &phase );
			glutPostRedisplay();
			break;
		case 'p':
		case 'P':
			if ( proj == PERSPECTIVE )
				proj = ORTHOGRAPHIC;
			else
			if ( proj == ORTHOGRAPHIC )
				proj = PERSPECTIVE;

			glutPostRedisplay();
			break;
		case 'i':
		case 'I':
			if ( fill_mode == GL_LINE )
				fill_mode = GL_FILL;
			else
			if ( fill_mode == GL_FILL )
				fill_mode = GL_POINT;
			else
			if ( fill_mode == GL_POINT )
				fill_mode = GL_LINE;

			glutPostRedisplay();
			break;
		default:
			break;
	}
}

/***************************************************************************\
 *                              Main                                       *
\***************************************************************************/

void commandlineerror( char *cmd, char *parm )
{
	if ( cmd )
		fprintf(stderr, "Offending option: %s\n", cmd );
	if ( parm )
		fprintf(stderr, "with first parameter: %s\n", parm );

	fprintf(stderr, "\n\nUsage: boxtree options ...\n"
	"Options:\n"
	"-w          with window; press space to continue with next test phase\n"
	"-h          show help\n"
	"Keys: (interactive version)\n"
	"i           polygon mode (wireframe, dots, filled)\n"
	"space       go to the next test case\n"
	"ESC         quit\n"
	"\n");

	if ( cmd )
		exit(-1);				// problem occured
	else
		exit(0);				// was option -h
}


void parsecommandline( int argc, char *argv[] )
{
	/* valid option characters; last char MUST be 0 ! */
	char optionchar[] =   { 'h', 'w', '\0' };
	int musthaveparam[] = {  0 ,  0  };
	int mhp[256];
	int isopt[256];
	char optchar;


	unsigned int nopts = strlen(optionchar);
	if ( nopts > 50 )
	{
		fprintf(stderr, "\n\nparsecommandline: the option-chars string "
				"seems to be\nVERY long (%d bytes) !\n\n", nopts );
		exit(-1);
	}

	fill_n( isopt, 256, 0 );
	fill_n( mhp, 256, 0 );
	for ( unsigned int i = 0; i < nopts; i ++ )
	{
		if ( isopt[static_cast<int>(optionchar[i])] )
		{
			fprintf(stderr, "\n\nparsecommandline: Bug: option character '%c'"
					" is specified twice in the\n"
							"option character array !\n\n", optionchar[i] );
			exit(-1);
		}
		isopt[ static_cast<int>(optionchar[i]) ] = 1;
		mhp[ static_cast<int>(optionchar[i])] = musthaveparam[i];
	}

	++argv; --argc;
	while ( argc > 0 )
	{
		if ( argv[0][0] == '-' )
		{
			optchar = argv[0][1];

			if ( ! isopt[static_cast<int>(optchar)] )
			{
				fprintf(stderr, "\nIs not a valid command line option\n");
				commandlineerror( argv[0], NULL );
			}
			for ( int i = 0; i < mhp[static_cast<int>(optchar)]; i ++ )
				if ( ! argv[1+i] || argv[1+i][0] == '-' )
				{
					fprintf(stderr, "\nCommand line option -%c must "
							"have %d parameter(s)\n",
							argv[0][1], mhp[static_cast<int>(optchar)] );
					commandlineerror( argv[0], NULL );
					argv += 1 + i;
					argc -= 1 + i;
					continue;
				}

			switch ( optchar )
			{
				case 'h': commandlineerror( NULL, NULL);  break;
				case 'w': with_window = true; break;
				default: fprintf(stderr, "\nBug in parsecommandline !\n");
						 commandlineerror( argv[0], NULL );
			}

			argv += 1 + mhp[static_cast<int>(optchar)];
			argc -= 1 + mhp[static_cast<int>(optchar)];
		}
		else
		{
			/* command line arg doesn't start with '-' */
			fprintf(stderr, "\nThis is not a valid command line option\n");
			commandlineerror( argv[0], NULL );
			/* or, load file instead .. */
		}
	}
}

int main(int argc, char** argv)
{
	// init help variable
	tri_prim.push_back( 0 );
	tri_prim.push_back( 1 );
	tri_prim.push_back( 2 );
	quad_prim.push_back( 0 );
	quad_prim.push_back( 1 );
	quad_prim.push_back( 2 );
	quad_prim.push_back( 3 );

	// parse command line options
	parsecommandline( argc, argv );

	// initialize global and static data
	DopTree::init();
	viewing_matrix = Matrix4::identity();

	// prepare glut
	glutInit( &argc, argv );
	glutInitDisplayMode ( GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGB );
	glutInitWindowSize( 350, 350 ); 
	glutInitWindowPosition( 100, 100 );
	glutCreateWindow( argv[0] );
	init();

	// register glut callback
	glutDisplayFunc( display ); 
	glutReshapeFunc( reshape );
	glutMouseFunc( mouseClick );
	glutMotionFunc( mouseMoving );
	glutKeyboardFunc( keyboard );

	// run...
	if ( with_window )
		// render loop begins
		glutMainLoop();
	else
	{
		int phase = 0;
		do
		{
			testcase( &phase );
		}
		while ( phase != 0 );
	}

	return 0;
}

